<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>3-React的生命周期（新）</title>
    </head>
    <body>
        <!-- 准备好DOM容器 -->
        <div id="test1"></div>
        <div id="test2"></div>

        <!-- 必须按照顺序引入JS -->
        <!-- 引入React核心库 -->
        <script src="../js/17/react.development.js"></script>
        <!-- 引入react-dom库，用于操作DOM -->
        <script src="../js/17/react-dom.development.js"></script>
        <!-- 引入babel，用于将JSX翻译为JS -->
        <script src="../js/17/babel.min.js"></script>

        <!-- type必须为text/babel，代表里面的内容为JSX -->
        <script type="text/babel">
            /**
             * 生命周期的三个阶段（新）
             *   1. 初始化阶段: 由ReactDOM.render()触发---初次渲染
             *      (1) constructor()
             *      (2) static getDerivedStateFromProps(props, state)
             *      (3) render()
             *      (4) componentDidMount() =====> 常用，一般在这个钩子中做一些初始化的事，例如：开启定时器、发送网络请求、订阅消息等
             *   2. 更新阶段: 由组件内部this.setSate()或父组件重新render触发
             *      (1) static getDerivedStateFromProps(props, state)
             *      (2) shouldComponentUpdate()
             *      (3) render() =====> 必须使用
             *      (4) getSnapshotBeforeUpdate()
             *      (5) componentDidUpdate(prevProps, prevState, snapshotValue)
             *   3. 卸载组件: 由ReactDOM.unmountComponentAtNode()触发
             *      (1) componentWillUnmount() =====> 常用，一般在这个钩子中做一些清理的事，例如：关闭定时器、取消消息订阅、关闭WebSocket连接等
             */
            class Count extends React.Component {
                /**
                 * 构造器
                 */
                constructor(props) {
                    console.log("Count --- constructor");
                    super(props);

                    // 初始化状态
                    this.state = { count: 0 };
                }

                increase = () => {
                    this.setState({ count: this.state.count + 1 });
                };

                death = () => {
                    ReactDOM.unmountComponentAtNode(
                        document.getElementById("test1")
                    );
                };

                force = () => {
                    this.forceUpdate();
                };

                /**
                 * 组件将要挂载的钩子
                 * React 17以后的版本弃用
                 */
                // UNSAFE_componentWillMount() {
                //     console.log("Count --- componentWillMount");
                // }

                /**
                 * 构造器执行完成后执行，必须使用静态方法
                 * 该方法的返回值必须为一个state对象或null
                 * 当返回一个state对象时，如果该对象中的属性在this.state中存在，则针对该属性的修改永远不生效（产生的效果相当于该属性变为只读）
                 * 只有当state中的值完全取决于props时才可以使用本生命周期钩子
                 */
                static getDerivedStateFromProps(props, state) {
                    console.log(
                        "Count --- getDerivedStateFromProps",
                        props,
                        state
                    );

                    return null;
                }

                /**
                 * 该生命周期钩子在render和componentDidUpdate之间触发
                 * 必须返回 null 或 快照值
                 */
                getSnapshotBeforeUpdate() {
                    console.log("Count --- getSnapshotBeforeUpdate");
                    return "snapshotValue";
                }

                /**
                 * 组件挂载完毕的钩子
                 */
                componentDidMount() {
                    console.log("Count --- componentDidMount");
                }

                /**
                 * 组件即将卸载的钩子
                 */
                componentWillUnmount() {
                    console.log("Count --- componentWillUnmount");
                }

                /**
                 * 控制组件更新的开关，必须返回true或false
                 * 每次调用setState()或接收到props后会调用此函数
                 * 若返回false，则不会再继续调用后续的componentWillUpdate和render钩子
                 */
                shouldComponentUpdate() {
                    console.log("Count --- shouldComponentUpdate");
                    return true;
                }

                /**
                 * 组件将要更新时的钩子
                 * React 17以后的版本弃用
                 */
                // UNSAFE_componentWillUpdate() {
                //     console.log("Count --- componentWillUpdate");
                // }

                /**
                 * 组件更新完成后的钩子
                 */
                componentDidUpdate(prevProps, prevState, snapshotValue) {
                    console.log(
                        "Count --- componentDidUpdate",
                        prevProps,
                        prevState,
                        snapshotValue
                    );
                }

                /**
                 * 组件初始渲染 和 状态更新之后
                 */
                render() {
                    console.log("Count --- render");
                    return (
                        <div>
                            <h2>当前求和为：{this.state.count}</h2>
                            <button onClick={this.increase}>点我+1</button>
                            <button onClick={this.death}>卸载组件</button>
                            <button onClick={this.force}>强制更新</button>
                        </div>
                    );
                }
            }

            // 父组件A
            class A extends React.Component {
                state = { carName: "奔驰" };

                changeCar = () => {
                    this.setState({ carName: "奥迪" });
                };

                render() {
                    return (
                        <div>
                            <div>我是A组件</div>
                            <button onClick={this.changeCar}>换车</button>
                            <B carName={this.state.carName} />
                        </div>
                    );
                }
            }

            // 子组件B
            class B extends React.Component {
                /**
                 * 组件将要接收新的props的钩子
                 * 在父组件初始化渲染（第一次调用render）时不触发，后续父组件再进行渲染（后续n次调用render）时触发
                 * React 17以后的版本弃用
                 */
                // UNSAFE_componentWillReceiveProps(props) {
                //     console.log("B --- componentWillReceiveProps", props);
                // }

                /**
                 * 控制组件更新的开关，必须返回true或false
                 * 每次调用setState()或接收到props后会调用此函数
                 * 若返回false，则不会再继续调用后续的componentWillUpdate和render钩子
                 */
                shouldComponentUpdate() {
                    console.log("B --- shouldComponentUpdate");
                    return true;
                }

                /**
                 * 组件将要更新时的钩子
                 * React 17以后的版本弃用
                 */
                // UNSAFE_componentWillUpdate() {
                //     console.log("B --- componentWillUpdate");
                // }

                /**
                 * 组件更新完成后的钩子
                 */
                componentDidUpdate(prevProps, prevState, snapshotValue) {
                    console.log(
                        "B --- componentDidUpdate",
                        prevProps,
                        prevState,
                        snapshotValue
                    );
                }

                render() {
                    console.log("B --- render");
                    return (
                        <div>我是B组件，接收到的车是：{this.props.carName}</div>
                    );
                }
            }

            ReactDOM.render(
                <Count count={199} />,
                document.getElementById("test1")
            );
            ReactDOM.render(<A />, document.getElementById("test2"));
        </script>
    </body>
</html>
